﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

using Utils;
using Utils.Services;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class InjectionServicesExtensions
    {

        /// <summary>
        /// 懒注入服务
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        public static IServiceCollection AddLazyService(this IServiceCollection services)
        {
            return services.AddTransient(typeof(Lazy<>), typeof(LazyService<>));
        }

        /// <summary>
        /// 自动导入
        /// </summary>
        /// <param name="services"></param>
        /// <param name="injectionFactory"></param>
        /// <param name="dllName"></param>
        /// <returns></returns>
        public static IServiceCollection AddInjection(this IServiceCollection services, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped, params string[] dllName)
        {
            Assembly defaultAssembly = Assembly.GetEntryAssembly();
            Union(dllName.Select(Assembly.Load).Prepend(defaultAssembly).ToArray())
                  .Select(typeInfo => AutoImport(services, typeInfo, serviceLifetime))
                  .ToArray();
            return services;
        }

        /// <summary>
        /// 并且注入只读属性
        /// </summary>
        /// <param name="services"></param>
        /// <param name="serviceType"></param>
        /// <returns></returns>
        public static IServiceCollection AddSingletonWithReadonly(this IServiceCollection services, Type serviceType)
        {
            return services.AddSingleton(serviceType, serviceProvider => Create(serviceProvider, serviceType));
        }
        /// <summary>
        /// 并且注入只读属性
        /// </summary>
        /// <param name="services"></param>
        /// <param name="serviceType"></param>
        /// <param name="implementationType"></param>
        /// <returns></returns>
        public static IServiceCollection AddSingletonWithReadonly(this IServiceCollection services, Type serviceType, Type implementationType)
        {
            return services.AddSingleton(serviceType, serviceProvider => Create(serviceProvider, implementationType));
        }
        /// <summary>
        /// 并且注入只读属性
        /// </summary>
        /// <param name="services"></param>
        /// <param name="serviceType"></param>
        /// <returns></returns>
        public static IServiceCollection AddScopedWithReadonly(this IServiceCollection services, Type serviceType)
        {
            return services.AddScoped(serviceType, serviceProvider => Create(serviceProvider, serviceType));
        }
        /// <summary>
        /// 并且注入只读属性
        /// </summary>
        /// <param name="services"></param>
        /// <param name="serviceType"></param>
        /// <param name="implementationType"></param>
        /// <returns></returns>
        public static IServiceCollection AddScopedWithReadonly(this IServiceCollection services, Type serviceType, Type implementationType)
        {
            return services.AddScoped(serviceType, serviceProvider => Create(serviceProvider, implementationType));
        }
        /// <summary>
        /// 并且注入只读属性
        /// </summary>
        /// <param name="services"></param>
        /// <param name="serviceType"></param>
        /// <returns></returns>
        public static IServiceCollection AddTransientWithReadonly(this IServiceCollection services, Type serviceType)
        {
            return services.AddTransient(serviceType, serviceProvider => Create(serviceProvider, serviceType));
        }
        /// <summary>
        /// 并且注入只读属性
        /// </summary>
        /// <param name="services"></param>
        /// <param name="serviceType"></param>
        /// <param name="implementationType"></param>
        /// <returns></returns>
        public static IServiceCollection AddTransientWithReadonly(this IServiceCollection services, Type serviceType, Type implementationType)
        {
            return services.AddTransient(serviceType, serviceProvider => Create(serviceProvider, implementationType));
        }


        private static IEnumerable<InjectionAttribute> FindTypeByAttribute(Assembly assembly)
        {
            foreach (var typeInfo in assembly.DefinedTypes)
            {
                var injection = typeInfo.GetCustomAttribute<InjectionAttribute>();
                if (injection != null)
                {
                    injection.SetImplementationType(typeInfo);
                    yield return injection;
                }
            }
        }

        private static IEnumerable<InjectionAttribute> Union(params Assembly[] assemblies)
        {
            return assemblies.SelectMany(assembly => FindTypeByAttribute(assembly));
        }

        private static void AddService(IServiceCollection services, Type serviceType, Type implementationType, ServiceLifetime serviceLifetime)
        {
            switch (serviceLifetime)
            {
                case ServiceLifetime.Singleton:
                    services.AddSingletonWithReadonly(implementationType);
                    if (serviceType != null)
                        services.AddSingletonWithReadonly(serviceType, implementationType);
                    break;
                case ServiceLifetime.Scoped:
                    services.AddScopedWithReadonly(implementationType);
                    if (serviceType != null)
                        services.AddScopedWithReadonly(serviceType, implementationType);
                    break;
                case ServiceLifetime.Transient:
                    services.AddTransientWithReadonly(implementationType);
                    if (serviceType != null)
                        services.AddTransientWithReadonly(serviceType, implementationType);
                    break;
                default:
                    break;
            }
        }


        /// <summary>
        /// 新增属性注入
        /// </summary>
        /// <param name="serviceProvider"></param>
        /// <param name="implementationType"></param>
        /// <returns></returns>
        private static object Create(IServiceProvider serviceProvider, Type implementationType)
        {
            object obj = ActivatorUtilities.CreateInstance(serviceProvider, implementationType);
            InjectionFactory.ImportsFormFactory(serviceProvider, obj);
            return obj;
        }




        /// <summary>
        /// 自动注入
        /// </summary>
        /// <param name="services"></param>
        /// <param name="typeInfo"></param>
        private static Type AutoImport(IServiceCollection services, InjectionAttribute injection, ServiceLifetime serviceLifetime)
        {
            AddService(services, injection.InterfaceType, injection.ImplementationType, injection.Lifetime ?? serviceLifetime);
            InjectionFactory._typeList.Add(injection.ImplementationType);
            return injection.InterfaceType;
        }
    }
}
